package com.koron.sinoprobe.web.plan.service.impl;

import com.alibaba.fastjson.JSON;
import com.koron.bean.system.staff.vo.EamUser;
import com.koron.bean.workflow.RedirectQuery;
import com.koron.common.core.business.workflow.bean.po.SysWorkflow;
import com.koron.common.core.business.workflow.bean.properties.WorkflowProperties;
import com.koron.common.core.business.workflow.service.SysWorkflowService;
import com.koron.common.core.business.workflow.templates.WorkFlowHandler;
import com.koron.common.core.config.ThreadLocalContext;
import com.koron.common.core.util.CodeTools;
import com.koron.sinoprobe.config.Constants;
import com.koron.sinoprobe.web.common.CommonUtils;
import com.koron.sinoprobe.web.ledger.bean.convertor.PlanConvertor;
import com.koron.sinoprobe.web.ledger.bean.vo.SpProjectVO;
import com.koron.sinoprobe.web.ledger.service.SpProjectService;
import com.koron.sinoprobe.web.plan.bean.entity.SpAssistDept;
import com.koron.sinoprobe.web.plan.bean.entity.SpMember;
import com.koron.sinoprobe.web.plan.bean.entity.SpPlan;
import com.koron.sinoprobe.web.plan.bean.entity.SpPlanMilestone;
import com.koron.sinoprobe.web.plan.bean.query.SpPlanQuery;
import com.koron.sinoprobe.web.plan.bean.vo.SpPlanPackageVO;
import com.koron.sinoprobe.web.plan.bean.vo.SpPlanVO;
import com.koron.sinoprobe.web.plan.mapper.*;
import com.koron.sinoprobe.web.plan.service.SpPlanService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.koron.ebs.mybatis.SessionFactory;
import org.koron.ebs.mybatis.TaskAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author twcao
 * @title: SpPlanServiceImpl
 * @projectName GDH_EAM_V2
 * @description: 专项计划service实现
 * @date 2021/11/2417:51
 */
@Slf4j
@Service
public class SpPlanServiceImpl implements SpPlanService {

    @Autowired
    private SysWorkflowService sysWorkflowService;

    @Autowired
    private SysWorkflowService workflowService;

    @Autowired
    private WorkFlowHandler workFlowHandler;

    @Autowired
    private SpProjectService projectService;

    @Autowired
    private PlanConvertor planConvertor;

    @Override
    @TaskAnnotation("selectOne")
    public SpPlanVO selectOne(SessionFactory factory, String planId) {
        SpPlanMapper mapper = factory.getMapper(SpPlanMapper.class);
        return mapper.queryDetailById(planId);
    }

    @Override
    @TaskAnnotation("queryList")
    public List<SpPlanVO> queryList(SessionFactory factory, SpPlanQuery query) {
        SpPlanMapper mapper = factory.getMapper(SpPlanMapper.class);
        return mapper.queryList(query);
    }

    @Override
    public List<SpPlanVO> getByPkgId(SessionFactory factory, String pkgId) {
        Assert.hasText(pkgId, "计划包id不能为空");
        return queryList(factory, new SpPlanQuery().setPkgId(pkgId).setDeleteFlag(null));
    }

    /**
     * 根据主办部门分组，并获取专项数量
     */
    @Override
    public List<Map<String, Object>> groupByPkgId(SessionFactory factory, String pkgId) {
        SpPlanMapper mapper = factory.getMapper(SpPlanMapper.class);
        // 查询预算类型
        List<SpPlan> spBudgetPlans = mapper.groupBudgetByPkgId(pkgId);
        // 根据hostDeptId分组
        Map<String, List<SpPlan>> budgetMap = Optional.ofNullable(spBudgetPlans)
                .map(list -> list.stream().collect(Collectors.groupingBy(SpPlan::getHostDeptId))).orElse(new HashMap<>());
        List<SpPlan> spPlans = mapper.groupByPkgId(pkgId);
        // 将isTeam作为总数量
        return spPlans.stream().map(spPlan -> {
            Map<String, Object> map = new HashMap<>();
            map.put("hostDeptId", spPlan.getHostDeptId());
            map.put("hostDeptName", spPlan.getHostDeptName());
            map.put("hostCount", spPlan.getIsTeam());
            List<SpPlan> budgetList = budgetMap.get(spPlan.getHostDeptId());
            List<Map<String, Object>> budgetCount = budgetList.stream().map(e -> {
                Map<String, Object> tempMap = new HashMap<>();
                tempMap.put("budget", e.getBudget());
                tempMap.put("budgetType", e.getBudgetType());
                return tempMap;
            }).collect(Collectors.toList());
            map.put("budgetCount", budgetCount);
            return map;
        }).collect(Collectors.toList());
    }

    /**
     * 更新专项计划（含计划关联的里程碑、协助部门、人员），如果里程碑、协助部门、人员为空，则会清空
     * 调用方法前，需要设置两个参数。code和deleteFlag
     * 计划包生成的计划：code为空避免占用编码)，deleteFlag = false
     * 在专项计划中生成的列表： code已分配，deleteFlag = true
     * @param factory
     * @param detail
     * @return
     */
    @Override
    @TaskAnnotation("saveOrUpdate")
    public SpPlanVO saveOrUpdate(SessionFactory factory, SpPlanVO detail) {
        Assert.notNull(detail, "专项计划不能为空");
        // 1、专项计划主体（新增或更新）
        SpPlanMapper mapper = factory.getMapper(SpPlanMapper.class);
        EamUser user = ThreadLocalContext.get();
        CommonUtils.update(detail);
        CommonUtils.update(detail, user);
        detail.setDeptId(StringUtils.isBlank(detail.getDeptId()) ? user.getOrgId() : detail.getDeptId());
        detail.setId(StringUtils.isEmpty(detail.getId()) ? CodeTools.getCode32() : detail.getId());
        detail.setSort(detail.getSort() == null ? 1 : detail.getSort());
        // 默认为草稿状态
        detail.setStatus(StringUtils.isBlank(detail.getStatus()) ? Constants.PlanStatus.DRAFT : detail.getStatus());
        if(StringUtils.isNotBlank(detail.getPkgId())) { //如果关联了计划包，确定计划包和计划的工作流是否共用
            SpPlanPackageMapper pkgMapper = factory.getMapper(SpPlanPackageMapper.class);
            SpPlanPackageVO vo = pkgMapper.queryById(detail.getPkgId());
            Assert.notNull(vo, "计划包不存在");
            // 若计划包为草稿状态时关联，则计划和计划包共用工作流
            detail.setAssistStatus(StringUtils.equals(vo.getStatus(), Constants.PackageStatus.DRAFT) ? Constants.PlanAssistStatus.SYNC : Constants.PlanAssistStatus.ASYNC);
        }
        mapper.saveOrUpdate(detail);
        // 2、协助部门
        SpAssistDeptMapper assistDeptMapper = factory.getMapper(SpAssistDeptMapper.class);
        List<SpAssistDept> assistDept = detail.getAssistDept();
        assistDeptMapper.deleteBySpId(detail.getId());
        if(!CollectionUtils.isEmpty(assistDept)) {
            assistDept.stream().forEach(row -> {
                row.setId(CodeTools.getCode32());
                row.setSpId(detail.getId());
                row.setCreateTime(new Date());
            });
            assistDeptMapper.addBatch(assistDept);
        }
        // 3、成员
        SpMemberMapper memberMapper = factory.getMapper(SpMemberMapper.class);
        List<SpMember> member = detail.getMember();
        memberMapper.deleteBySpId(detail.getId());
        if(!CollectionUtils.isEmpty(member)) {
            CommonUtils.sorted(member);
            member.stream().forEach(row -> {
                row.setId(CodeTools.getCode32());
                row.setSpId(detail.getId());
                row.setCreateTime(new Date());
            });
            memberMapper.addBatch(member);
        }
        // 4、里程碑
        SpPlanMilestoneMapper milestoneMapper = factory.getMapper(SpPlanMilestoneMapper.class);
        List<SpPlanMilestone> milestone = detail.getMilestone();
        Assert.notEmpty(milestone, "里程碑不能为空");
        milestoneMapper.deleteBySpId(detail.getId());
        CommonUtils.sorted(milestone);
        milestone.stream().forEach(row -> {
            row.setId(CodeTools.getCode32());
            row.setSpId(detail.getId());
            row.setCreateTime(new Date());
        });
        milestoneMapper.addBatch(milestone);
        return detail;
    }

    @Override
    @TaskAnnotation("saveOrUpdateBatch")
    public boolean saveOrUpdateBatch(SessionFactory factory, String pkgId, List<SpPlan> list) {
        EamUser user = ThreadLocalContext.get();
        list.forEach(detail -> {
            Assert.notNull(detail, "专项计划不能为空");
            // 1、专项计划主体（新增或更新）
            CommonUtils.update(detail, user);
            detail.setDeleteFlag(true);
            detail.setPkgId(pkgId);
            detail.setDeptId(StringUtils.isBlank(detail.getDeptId()) ? user.getOrgId() : detail.getDeptId());
            detail.setId(StringUtils.isEmpty(detail.getId()) ? CodeTools.getCode32() : detail.getId());
            detail.setSort(detail.getSort() == null ? 1 : detail.getSort());
            // 默认为草稿状态
            detail.setStatus(StringUtils.isBlank(detail.getStatus()) ? Constants.PlanStatus.DRAFT : detail.getStatus());
            // 计划包生成
            detail.setPlanSource(Constants.PlanSource.PLAN_PACKAGE);
            // 计划跟计划包同步走流程
            detail.setAssistStatus(Constants.PlanAssistStatus.SYNC);
        });
        SpPlanMapper mapper = factory.getMapper(SpPlanMapper.class);
        return mapper.saveOrUpdateBatch(list) > 0;
    }

    /**
     * 进更新基础信息
     * @param factory
     * @param plan
     * @return
     */
    @Override
    @TaskAnnotation("update")
    public SpPlan update(SessionFactory factory, SpPlan plan) {
        Assert.notNull(plan, "专项计划不能为空");
        Assert.hasText(plan.getId(), "专项计划id不能为空");
        // 1、专项计划主体（新增或更新）
        SpPlanMapper mapper = factory.getMapper(SpPlanMapper.class);
        CommonUtils.update(plan);
        CommonUtils.update(plan, ThreadLocalContext.get());
        plan.setSort(plan.getSort() == null ? 1 : plan.getSort());
        // 默认为草稿状态
        plan.setStatus(StringUtils.isBlank(plan.getStatus()) ? Constants.PlanStatus.DRAFT : plan.getStatus());
        mapper.saveOrUpdate(plan);
        return plan;
    }

    @Override
    @TaskAnnotation("submit")
    public SpPlanVO submit(SessionFactory factory, SpPlanVO detail) {
        // 由计划包生成的计划，不允许提交，且无审批流程
        Assert.isTrue(detail != null && StringUtils.isBlank(detail.getPkgId()), "专项计划包生成的计划，不需要审核");
        // 1、保存专项计划
        // 设置为待审核
        detail.setStatus(Constants.PlanStatus.WAIT_REVIEW);
        detail.setCode(StringUtils.isEmpty(detail.getCode()) ? CommonUtils.getCodeNo(Constants.SP_PREFIX_CODE, 3) : detail.getCode());
        saveOrUpdate(factory, detail);
        // 2、创建工作流
        List<SysWorkflow> sysWorkflows = workflowService.queryByDataId(detail.getId());
        if(!CollectionUtils.isEmpty(sysWorkflows)) {
            return detail;
        }
        // 指定下个节点审批人id
        Map<String, String> formVariables = new HashMap<>(2);
        WorkflowProperties prop = WorkflowProperties.init(Constants.SP_PLAN_WORKFLOW.PROCESS_CODE);
        SysWorkflow workflow = workFlowHandler.start(prop, detail.getId(), detail.getCode(), detail.getName(), formVariables);
        log.info("工作流创建成功， billId = {}", workflow.getBillId());
        return detail;
    }

    @Override
    @TaskAnnotation("auditFinish")
    public SpPlanVO auditFinish(SessionFactory factory, SpPlanVO detail) {
        // 1、工作流结束后，专项计划设置为已审批
        detail.setStatus(Constants.PlanStatus.APPROVED);
        // 2、保存修改状态
        detail = saveOrUpdate(factory, detail);
        // 3、TODO: 将计划包的审批流，为计划复制一份
        return detail;
    }

    /**
     * 下发生成专项台账
     * @param factory
     * @param planId
     * @return
     */
    @Override
    @TaskAnnotation("issue")
    public SpPlanVO issue(SessionFactory factory, String planId) {
        SpPlanVO planVO = selectOne(factory, planId);
        SpPlanMapper mapper = factory.getMapper(SpPlanMapper.class);
        Assert.notNull(planVO, "专项计划不存在");
        Assert.isTrue(StringUtils.equals(planVO.getStatus(), Constants.PlanStatus.APPROVED), "只能下发已审核的专项计划");
        SpProjectVO projectVO = planConvertor.plaVOnToProjectVO(planVO);
        log.info("专项计划下发开始: {}", JSON.toJSONString(projectVO));
        projectService.saveOrUpdate(factory, projectVO);
        log.info("专项计划下发完成: {}", JSON.toJSONString(projectVO));
        planVO.setStatus(Constants.PlanStatus.ISSUED);
        //saveOrUpdate(factory, planVO);
        mapper.updateStatusById(planId, planVO.getStatus());
        return planVO;
    }

    @Override
    @TaskAnnotation("hiddenById")
    public String hiddenById(SessionFactory factory, String planId) {
        SpPlanMapper mapper = factory.getMapper(SpPlanMapper.class);
        mapper.hiddenById(planId);
        workFlowHandler.delete(planId);
        return planId;
    }

    @Override
    @TaskAnnotation("hiddenByPkgId")
    public String hiddenByPkgId(SessionFactory factory, String pkgId) {
        SpPlanMapper mapper = factory.getMapper(SpPlanMapper.class);
        mapper.hiddenByPkgId(pkgId);
        List<SpPlanVO> spPlanVOS = mapper.queryList(new SpPlanQuery().setPkgId(pkgId));
        // 删除计划包内计划的工作流
        spPlanVOS.stream().map(SpPlanVO::getId).forEach(row -> workFlowHandler.delete(row));
        return pkgId;
    }

    @Override
    public Boolean deleteById(SessionFactory factory, String planId) {
        SpPlanMapper mapper = factory.getMapper(SpPlanMapper.class);
        mapper.deleteById(planId);
        SpAssistDeptMapper assistDeptMapper = factory.getMapper(SpAssistDeptMapper.class);
        assistDeptMapper.deleteBySpId(planId);
        SpMemberMapper spMemberMapper = factory.getMapper(SpMemberMapper.class);
        spMemberMapper.deleteBySpId(planId);
        SpPlanMilestoneMapper milestoneMapper = factory.getMapper(SpPlanMilestoneMapper.class);
        milestoneMapper.deleteBySpId(planId);
        // 删除计划的工作流
        workFlowHandler.delete(planId);
        return true;
    }

    @Override
    @TaskAnnotation("workflowUpdate")
    public Boolean workflowUpdate(SessionFactory factory, RedirectQuery redirectQuery) {
        Integer operation = redirectQuery.getOperation();
        SpPlanVO vo = JSON.parseObject(redirectQuery.getFormVariables(), SpPlanVO.class);
        SpPlanMapper mapper = factory.getMapper(SpPlanMapper.class);
        switch (operation) {
            case 256: // 撤回
            case 1: // 驳回
                // 驳回的节点编号
                vo.setStatus(Constants.PlanStatus.DRAFT);
                //saveOrUpdate(factory, vo);
                break;
            case 32: // 同意
                vo.setStatus(Constants.PlanStatus.WAIT_REVIEW);
                //saveOrUpdate(factory, vo);
                break;
        }
        // 更新状态
        mapper.updateStatusById(vo.getId(), vo.getStatus());
        return true;
    }

    /**
     * 专项计划包状态变更
     * @param factory   会话
     * @param pkgId 专项计划包id
     * @param pkgStatus 变更后的状态
     * @return
     */
    @Override
    public Boolean updateStatus(SessionFactory factory, String pkgId, String pkgStatus) {
        if(StringUtils.isBlank(pkgId)) {
            return true;
        }
        SpPlanMapper planMapper = factory.getMapper(SpPlanMapper.class);
        // 获取计划包下的所有计划
        List<SpPlanVO> plans = planMapper.queryList(new SpPlanQuery().setPkgId(pkgId).setDeleteFlag(null));
        switch (pkgStatus) {
            case Constants.PackageStatus.DRAFT:
                // 可能为提交后撤回
                plans.stream().forEach(plan -> {
                    // 1、更新工作流状态
                    plan.setStatus(Constants.PlanStatus.DRAFT);
                    //update(factory, plan);
                    planMapper.updateStatusById(plan.getId(), plan.getStatus());
                });
                break;
            case Constants.PackageStatus.WAIT_REVIEW:
                plans.stream().forEach(plan -> {
                    // 1、更新工作流状态
                    plan.setStatus(Constants.PlanStatus.WAIT_REVIEW);
                    planMapper.updateStatusById(plan.getId(), plan.getStatus());
                    if(StringUtils.isNotBlank(plan.getCurrNodeName())) {
                        // 如果已经存在节点名称，说明已经有工作流了，则不再创建工作流(当提交后，撤回或驳回到草稿状态时，会进入此判断)
                        return;
                    }
                    // 2、 复制工作流（该专项计划包内的所有计划，与计划包共用一个工作流）
                    SysWorkflow workflow = new SysWorkflow();
                    workflow.setDataId(pkgId);
                    List<SysWorkflow> sysWorkflows = sysWorkflowService.queryAll(workflow);
                    Assert.isTrue(CollectionUtils.size(sysWorkflows) > 0, "专项计划包工作流不存在");
                    // 同一个billId对应多个dataId，即同一个工作流对应多个业务单
                    SysWorkflow sysWorkflow = sysWorkflows.get(0);
                    sysWorkflow.setDataId(plan.getId());
                    sysWorkflow.setModuleCode(null);
                    sysWorkflowService.insert(sysWorkflow);
                });
                break;
            case Constants.PackageStatus.APPROVED:
                plans.stream().forEach(plan -> {
                    // 1、更新工作流状态
                    plan.setStatus(Constants.PlanStatus.APPROVED);
                    //update(factory, plan);
                    planMapper.updateStatusById(plan.getId(), plan.getStatus());
                });
                break;
        }
        return true;
    }
}
